﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;

namespace KAImage
{
    public static class ImageAdjustments
    {
        public static Bitmap Contrast(this Bitmap sourceBitmap, int threshold)
        {
            BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0,
                                        sourceBitmap.Width, sourceBitmap.Height),
                                        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
            Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
            sourceBitmap.UnlockBits(sourceData);
            double contrastLevel = Math.Pow((100.0 + threshold) / 100.0, 2);
            double blue = 0;
            double green = 0;
            double red = 0;
            for (int k = 0; k + 4 < pixelBuffer.Length; k += 4)
            {
                blue = ((((pixelBuffer[k] / 255.0) - 0.5) *
                            contrastLevel) + 0.5) * 255.0;
                green = ((((pixelBuffer[k + 1] / 255.0) - 0.5) *
                            contrastLevel) + 0.5) * 255.0;
                red = ((((pixelBuffer[k + 2] / 255.0) - 0.5) *
                            contrastLevel) + 0.5) * 255.0;
                if (blue > 255)
                { blue = 255; }
                else if (blue < 0)
                { blue = 0; }
                if (green > 255)
                { green = 255; }
                else if (green < 0)
                { green = 0; }
                if (red > 255)
                { red = 255; }
                else if (red < 0)
                { red = 0; }
                pixelBuffer[k] = (byte)blue;
                pixelBuffer[k + 1] = (byte)green;
                pixelBuffer[k + 2] = (byte)red;
            }
            Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
            BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0,
                                        resultBitmap.Width, resultBitmap.Height),
                                        ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
            Marshal.Copy(pixelBuffer, 0, resultData.Scan0, pixelBuffer.Length);
            resultBitmap.UnlockBits(resultData);
            return resultBitmap;
        } 

        public static ImageFormat GetImageFormat(string filePath)
        {
            string ext = Path.GetExtension(filePath);
            int start = ext.LastIndexOf(".");
            ext = ext.Substring(start + 1, ext.Length - start - 1);
            ext = ext.ToUpper();          
            ImageFormat f = ImageFormat.Jpeg;
            switch (ext)
            {
                case "JPG":
                    f = ImageFormat.Jpeg;
                    break;
                case "PNG":
                    f = ImageFormat.Png;
                    break;
                case "BMP":
                    f = ImageFormat.Bmp;
                    break;
                default:
                    break;
            }
            return f;
        }

        public static void ApplyMappingToRGB(Bitmap source, int[] mapR, int[] mapG, int[] mapB)
        {
            int iwidth = source.Width;
            int iheight = source.Height;
            BitmapData imData = source.LockBits(new Rectangle(0, 0, iwidth, iheight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride = imData.Stride;
            System.IntPtr Scan0 = imData.Scan0;
            int b = 0, g = 0, r = 0;
            unsafe
            {
                byte* pS = (byte*)(void*)Scan0;
                int nOffset = stride - iwidth * 3;
                for (int y = 0; y < iheight; ++y)
                {
                    for (int x = 0; x < iwidth; ++x)
                    {
                        b = pS[0];
                        g = pS[1];
                        r = pS[2];

                        pS[0] = (byte)mapB[b];
                        pS[1] = (byte)mapG[g];
                        pS[2] = (byte)mapR[r];

                        pS += 3;
                    }
                    pS += nOffset;
                }
            }
            source.UnlockBits(imData);
        }

        public static void ApplyMappingToRGB(Bitmap source, int[] mapping)
        {
            ApplyMappingToRGB(source, mapping, mapping, mapping);
        }

        private static void ApplyMappingToSingleChannel(Bitmap source, int[] mapping, int index = 0)
        {
            int iwidth = source.Width;
            int iheight = source.Height;
            BitmapData imData = source.LockBits(new Rectangle(0, 0, iwidth, iheight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride = imData.Stride;
            System.IntPtr Scan0 = imData.Scan0;
            int pv = 0;
            unsafe
            {
                byte* pS = (byte*)(void*)Scan0;
                int nOffset = stride - iwidth * 3;
                for (int y = 0; y < iheight; ++y)
                {
                    for (int x = 0; x < iwidth; ++x)
                    {
                        pv = pS[index];                      
                        pS[index] = (byte)mapping[pv];                     
                        pS += 3;
                    }
                    pS += nOffset;
                }
            }
            source.UnlockBits(imData);
        }

        public static void ApplyMappingToB(Bitmap source, int[] mapping)
        {
            ApplyMappingToSingleChannel(source, mapping, 0);
        }

        public static void ApplyMappingToG(Bitmap source, int[] mapping)
        {
            ApplyMappingToSingleChannel(source, mapping, 1);
        }

        public static void ApplyMappingToR(Bitmap source, int[] mapping)
        {
            ApplyMappingToSingleChannel(source, mapping, 2);
        }
    }
}
